//////////////////////////////////////////////////////////////////////////
// $Id: Mesh.cpp,v 1.1 2009/04/03 12:46:10 sunjun Exp $
//////////////////////////////////////////////////////////////////////////

#include "Local.h"

//////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////////

NAMESPACE_BEGIN

//////////////////////////////////////////////////////////////////////////	

	Mesh::Mesh()
	{
	
	}

	Mesh::~Mesh()
	{
		// clear all of the bones
		mBones.clear();

		// clear all of the batches
		mBatches.clear();
	}

	//////////////////////////////////////////////////////////////////////////

	/*
	====================
	bind
	====================
	*/
	INLINE VOID Mesh::bind( const Context * rc )
	{
		CHECK( mRCPtr == NULL );
		mRCPtr = (Context*)rc;
	}

	/*
	====================
	load
	====================
	*/
	INLINE VOID Mesh::load( const MData* data )
	{
		CHECK(data);
		CHECK(mRCPtr);

		// get the mesh config
		MConfigPtr mesh_config_ptr = MConfig::load(data);
		CHECK( mesh_config_ptr );

		// check it if it is a mesh?
		CHECK( Str(mesh_config_ptr->getValue()) == "mesh" );

		// prase the effect`s children
		const MConfig* mesh_child_config_ptr = NULL;
		for( U32 i = 0; mesh_child_config_ptr = mesh_config_ptr->getChild(i); i++ )
		{
			// prase the bone
			if( Str(mesh_child_config_ptr->getValue()) == "bone" )
			{
				Bone bone;
				bone.source = NULL;

				// get the bone name
				Str bone_name = mesh_child_config_ptr->getAttribute("name"); 
				CHECK(bone_name != "");

				// get the base pose of the bone
				Matrix pose;
				std::vector<Str>tokens = MTokenize(mesh_child_config_ptr->getText());
				CHECK(tokens.size() == 16);
				for(U32 j = 0; j < 16; j++)bone.base.ptr()[j] = ::atof(tokens[j].c_str());
				bone.base.invert(bone.base);

				// add the bone to the table
				mBones.insert( std::map< Str, Bone >::value_type(bone_name, bone) );
			}
			// prase the batch
			else if( Str(mesh_child_config_ptr->getValue()) == "batch" )
			{
				Batch batch;

				// the primitive of the batch
				batch.primitive = MNEW(Primitive);
				CHECK(batch.primitive);
				batch.primitive->setPrimitiveType(Primitive::PT_TRIANGLELIST);

				// the count of the vertex
				U32 vertex_count = 0;

				// the vertex format
				U32 stream = 0;
				std::vector<VertexFormat::FORMAT>vertex_formats;

				// prase the batch`s children
				const MConfig *batch_child_config_ptr = NULL;
				for( U32 j = 0; batch_child_config_ptr = mesh_child_config_ptr->getChild(j); j++ )
				{
					// prase the bone in the batch
					if( Str(batch_child_config_ptr->getValue()) == "bone" )
					{
						// get the bone name
						Str bone_name = batch_child_config_ptr->getAttribute("name"); 
						CHECK(bone_name != "");

						// set the bone
						std::map< Str, Bone >::iterator it = mBones.find(bone_name);
						if( it != mBones.end() )
						{
							batch.bones.push_back( &it->second.pose );
						}
						else
						{
							batch.bones.push_back(NULL);
						}
					}
					else if( Str(batch_child_config_ptr->getValue()) == "effect" )
					{
						const MConfig *effect_config_ptr = (const MConfig *)batch_child_config_ptr;

						// get the shader name
						Str effect_name = effect_config_ptr->getAttribute("name"); 
						CHECK(effect_name!= "");

						// set the effect
						Dict::KeyPtr effect_key_ptr = Dict::instance().find(effect_name.c_str());
						if( effect_key_ptr == NULL )
						{
							// the new effect
							EffectPtr effect_ptr = MNEW(Effect(mRCPtr.ptr()));
							CHECK(effect_ptr);

							// read the effect file
							MFile file;
							CHECK(file.open(effect_name.c_str()));
							U32 pos = file.tell();
							file.seek(0,2);
							U32 size = file.tell();
							file.seek(pos,0);
							MDataPtr data_ptr = MNEW(MData);
							CHECK(data_ptr);
							data_ptr->resize(size);
							CHECK(file.read(data_ptr.ptr())==size);
							file.close();

							// load the effect data
							effect_ptr->load(data_ptr->get(), data_ptr->size());

							// hold the key for the resource
							effect_key_ptr = MNEW( Dict::Key(effect_name.c_str(), effect_ptr.ptr()) ); 
						}
						CHECK(effect_key_ptr);

						// add the effect key to the key map
						mKeys.push_back(effect_key_ptr);						

						// set the effect for the primitive
						batch.primitive->setEffect(dynamic_cast<Effect*>(effect_key_ptr->ptr()));

						// parse the constant of the shader
						const MConfig *constant_config_ptr = NULL;
						for( U32 k = 0; constant_config_ptr = effect_config_ptr->getChild(k); k++ )
						{
							CHECK( Str(constant_config_ptr->getValue()) == "constant" );

							// get the name of the constant
							Str name = constant_config_ptr->getAttribute("name"); CHECK(name!= "");

							// get the type of the constant
							Str type = constant_config_ptr->getAttribute("type"); CHECK(type!= "");

							// get the value of the constant
							Str value = constant_config_ptr->getText(); CHECK(value!= "");
							
							// if it is a global constant
							ConstantPtr constant_ptr;
							Dict::KeyPtr constant_key_ptr = Dict::instance().find(name.c_str());
							if( constant_key_ptr )
							{
								constant_ptr = dynamic_cast<Constant*>(constant_key_ptr->ptr());
								CHECK( constant_ptr );
							}
							else
							{
								// the new constant
								constant_ptr = MNEW(Constant); 
								CHECK( constant_ptr );

								// get the constant type and the value
								if( type == "CT_BOOL" )
								{
									if(value == "FALSE") constant_ptr->setBool(FALSE);
									else if(value == "TRUE") constant_ptr->setBool(TRUE);
									else MAssert( VA("The BOOL type value(%s) is error.\n", value.c_str() ) );
								}
								else if( type == "CT_BOOL_ARRAY" )
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									std::vector<BOOL>data;			
									for ( U32 l = 0; l < count; l++ ) 
									{
										if(tokens[l] == "FALSE") data.push_back(FALSE);
										else if(tokens[l] == "TRUE") data.push_back(TRUE);
										else MAssert( VA("The BOOL type value(%s) is error.\n", tokens[l].c_str() ) );
									}
									constant_ptr->setBoolArray((const BOOL*)&data[0],count);
								}
								else if( type == "CT_INT" )
								{
									constant_ptr->setInt(::atoi(value.c_str()));
								}
								else if( type == "CT_INT_ARRAY" ) 
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									std::vector<I32>data;
									for( U32 l = 0; l < count; l++ ) data.push_back(::atoi( tokens[l].c_str()));
									constant_ptr->setIntArray(&data[0],count);
								}
								else if( type == "CT_FLOAT" )
								{
									constant_ptr->setInt(::atof(value.c_str()));
								}
								else if( type == "CT_FLOAT_ARRAY" )
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									std::vector<F32>data;
									for( U32 l = 0; l < count; l++ ) data.push_back(::atof( tokens[l].c_str()));
									constant_ptr->setFloatArray(&data[0],count);
								}
								else if( type == "CT_VECTOR" )
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									CHECK( count == 4 );
									Vector4 vec;
									vec[0] = ::atof(tokens[0].c_str());
									vec[1] = ::atof(tokens[1].c_str());
									vec[2] = ::atof(tokens[2].c_str());
									vec[3] = ::atof(tokens[3].c_str());
									constant_ptr->setVector(vec);
								}
								else if( type == "CT_VECTOR_ARRAY" )
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									CHECK( count % 4 == 0 );
									std::vector<F32>data;
									for(U32 l = 0; l < count; l++) data.push_back(::atof( tokens[l].c_str()));
									count = count / 4;
									constant_ptr->setVectorArray((const Vector4*)&data[0], count);
								}
								else if( type == "CT_MATRIX" ) 
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									CHECK( count == 16 );
									Matrix mat;
									for(U32 l = 0; l < count; l++) mat.ptr()[l] = ::atof(tokens[l].c_str());
									constant_ptr->setMatrix(mat);
								}
								else if( type == "CT_MATRIX_ARRAY" ) 
								{
									std::vector<Str>tokens = MTokenize( value );
									U32 count = tokens.size();
									CHECK( count % 16 == 0 );
									std::vector<F32>data;			
									for(U32 l = 0; l < count; l++) data.push_back(::atof(tokens[l].c_str()));
									count = count / 16;
									constant_ptr->setMatrixArray((const Matrix*)&data[0], count);
								}
								else if( type == "CT_STRING" ) 
								{
									// set the string									
									constant_ptr->setString(value.c_str());
								}
								else if( type == "CT_TEXTURE" ) 
								{
									// set the texture
									Dict::KeyPtr texture_key_ptr = Dict::instance().find(value.c_str());
									if( texture_key_ptr == NULL )
									{
										// the new texture
										TexturePtr texture_ptr = MNEW(Texture(mRCPtr.ptr()));
										CHECK(texture_ptr);

										// read the texture file
										MFile file;
										CHECK(file.open(value.c_str()));
										U32 pos = file.tell();
										file.seek(0,2);
										U32 size = file.tell();
										file.seek(pos,0);
										MDataPtr data_ptr = MNEW(MData);
										CHECK(data_ptr);
										data_ptr->resize(size);
										CHECK(file.read(data_ptr.ptr())==size);
										file.close();

										// load the texture
										texture_ptr->load( data_ptr->get(), data_ptr->size() );

										// hold the key for the resource
										texture_key_ptr = MNEW(Dict::Key(name.c_str(), texture_ptr.ptr()));
									}
									CHECK(texture_key_ptr);
									constant_ptr->setTexture(dynamic_cast<Texture*>(texture_key_ptr->ptr()));

									// add the texture key to the key map
									mKeys.push_back(texture_key_ptr);
								}
								else MAssert( VA("The type(%s) of the constant(%s) is error.\n", name.c_str(), type.c_str() ) );								
							}							

							// set the constant for the primitive
							batch.primitive->setConstant( name.c_str(), constant_ptr.ptr() );
						}
					}
					else if( Str(batch_child_config_ptr->getValue()) == "vertex" )
					{
						std::vector<VertexFormat::FORMAT>formats;
						U32 elem_count = 0, elem_size = 0;

						// get the vertex type and semantic
						std::vector<Str>types = MTokenize(batch_child_config_ptr->getAttribute("type"));
						std::vector<Str>semantics = MTokenize(batch_child_config_ptr->getAttribute("semantic"));
						CHECK(types.size() == semantics.size());
						U32 size = types.size();
						for(U32 k = 0; k < size; k++)
						{
							// the new vertex format
							VertexFormat::FORMAT format; 
							format.stream = stream;
							format.offset = elem_size;

							// get the vertex type
							if(types[k]=="VET_FLOAT1") format.type = VertexFormat::VET_FLOAT1, elem_count+=1, elem_size+=1*sizeof(F32);
							else if(types[k]=="VET_FLOAT2") format.type = VertexFormat::VET_FLOAT2, elem_count+=2, elem_size+=2*sizeof(F32);
							else if(types[k]=="VET_FLOAT3") format.type = VertexFormat::VET_FLOAT3, elem_count+=3, elem_size+=3*sizeof(F32);
							else if(types[k]=="VET_FLOAT4") format.type = VertexFormat::VET_FLOAT4, elem_count+=4, elem_size+=4*sizeof(F32);
							else if(types[k]=="VET_COLOR") format.type = VertexFormat::VET_COLOR, elem_count+=1, elem_size+=sizeof(U32);
							else if(types[k]=="VET_UBYTE4") format.type = VertexFormat::VET_UBYTE4, elem_count+=4, elem_size+=4*sizeof(U8);
							else if(types[k]=="VET_SHORT2") format.type = VertexFormat::VET_SHORT2, elem_count+=2, elem_size+=2*sizeof(I16);
							else if(types[k]=="VET_SHORT4") format.type = VertexFormat::VET_SHORT4, elem_count+=4, elem_size+=4*sizeof(I16);
							else if(types[k]=="VET_UBYTE4N") format.type = VertexFormat::VET_UBYTE4N, elem_count+=4, elem_size+=4*sizeof(U8);
							else if(types[k]=="VET_SHORT2N") format.type = VertexFormat::VET_SHORT2N, elem_count+=2, elem_size+=2*sizeof(I16);
							else if(types[k]=="VET_SHORT4N") format.type = VertexFormat::VET_SHORT4N, elem_count+=4, elem_size+=4*sizeof(I16);
							else if(types[k]=="VET_USHORT2N") format.type = VertexFormat::VET_USHORT2N, elem_count+=2, elem_size+=2*sizeof(U16);
							else if(types[k]=="VET_USHORT4N") format.type = VertexFormat::VET_USHORT4N, elem_count+=4, elem_size+=4*sizeof(U16);
							else MAssert( VA("The vertex`s type(%s) is unknown!", types[k].c_str()) );

							// get the vertex semantic
							if(semantics[k]=="VES_POSITION") format.semantic = VertexFormat::VES_POSITION;
							else if(semantics[k]=="VES_BLENDWEIGHT") format.semantic = VertexFormat::VES_BLENDWEIGHT;
							else if(semantics[k]=="VES_BLENDINDICES") format.semantic = VertexFormat::VES_BLENDINDICES;
							else if(semantics[k]=="VES_NORMAL") format.semantic = VertexFormat::VES_NORMAL;
							else if(semantics[k]=="VES_PSIZE") format.semantic = VertexFormat::VES_PSIZE;
							else if(semantics[k]=="VES_TEXCOORD") format.semantic = VertexFormat::VES_TEXCOORD;
							else if(semantics[k]=="VES_TANGENT") format.semantic = VertexFormat::VES_TANGENT;
							else if(semantics[k]=="VES_BINORMAL") format.semantic = VertexFormat::VES_BINORMAL;
							else if(semantics[k]=="VES_TESSFACTOR") format.semantic = VertexFormat::VES_TESSFACTOR;
							else if(semantics[k]=="VES_POSITIONT") format.semantic = VertexFormat::VES_POSITIONT;
							else if(semantics[k]=="VES_COLOR") format.semantic = VertexFormat::VES_COLOR;
							else if(semantics[k]=="VES_FOG") format.semantic = VertexFormat::VES_FOG;
							else if(semantics[k]=="VES_DEPTH") format.semantic = VertexFormat::VES_DEPTH;
							else if(semantics[k]=="VES_SAMPLE") format.semantic = VertexFormat::VES_SAMPLE;
							else MAssert( VA("The vertex`s semantic(%s) is unknown!", semantics[k].c_str()) );

							// add the format table
							formats.push_back(format);
						}						

						// get the vertex data
						std::vector<Str>tokens = MTokenize(batch_child_config_ptr->getText());
						Str*token = &tokens[0];
						size = tokens.size();
						CHECK(size % elem_count == 0);
						U32 count = size/elem_count;
						if(vertex_count == 0) vertex_count = count;
						CHECK(vertex_count==count);
						std::vector<U8>data(count*elem_size);
						U8*ptr = &data[0];
						U32 format_size = formats.size();
						for(U32 k = 0; k < count; k++)
						{
							for( U32 m = 0; m < format_size; m++ )
							{
								switch(formats[m].type)
								{
								case VertexFormat::VET_FLOAT1:
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									break;
								case VertexFormat::VET_FLOAT2:
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									break;
								case VertexFormat::VET_FLOAT3:
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									break;
								case VertexFormat::VET_FLOAT4:
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									(*((F32*)ptr)) = ::atof(token->c_str()), ptr+=sizeof(F32), token++;
									break;
								case VertexFormat::VET_COLOR:
									(*((U32*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U32), token++;
									break;
								case VertexFormat::VET_UBYTE4:
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									break;
								case VertexFormat::VET_SHORT2:
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									break;
								case VertexFormat::VET_SHORT4:
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									break;
								case VertexFormat::VET_UBYTE4N:
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									(*((U8*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U8), token++;
									break;
								case VertexFormat::VET_SHORT2N:
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									break;
								case VertexFormat::VET_SHORT4N:
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									(*((I16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(I16), token++;
									break;
								case VertexFormat::VET_USHORT2N:
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									break;
								case VertexFormat::VET_USHORT4N:
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									(*((U16*)ptr)) = ::atoi(token->c_str()), ptr+=sizeof(U16), token++;
									break;
								default:
									MAssert( VA("The vertex`s type(%s) is unknown!", types[k].c_str()) );
									break;
								}
							}
						}

						// set the vertex buffer for the primitive
						VertexBufferPtr vb_ptr = MNEW(VertexBuffer(mRCPtr.ptr())); CHECK(vb_ptr);
						vb_ptr->load(&data[0],data.size(),elem_size);
						batch.primitive->setVertexBuffer(vb_ptr.ptr(), stream);

						// add the vertex format to the table
						for( U32 m = 0; m < format_size; m++ ) vertex_formats.push_back(formats[m]);

						// the next stream
						stream++;
					}
					else if( Str(batch_child_config_ptr->getValue()) == "index" )
					{
						// get the index data
						std::vector<Str>tokens = MTokenize(batch_child_config_ptr->getText());
						U32 size = tokens.size();
						std::vector<U16>indexes(size);
						for(U32 k = 0; k < size; k++)indexes[k] = ::atoi(tokens[k].c_str());

						// set the index buffer for the primitive
						IndexBufferPtr ib_ptr = MNEW(IndexBuffer(mRCPtr.ptr())); CHECK(ib_ptr);
						ib_ptr->load(&indexes[0],indexes.size(),sizeof(U16));
						batch.primitive->setIndexBuffer(ib_ptr.ptr());
					}
					else
					{
						MAssert( VA("The batch`s keyword(%s) is unknown!", batch_child_config_ptr->getValue()) );
					}
				}

				// set the vertex formats of the primitive
				VertexFormat::FORMAT format; 
				format.stream = 0xFF;				
				vertex_formats.push_back(format);
				VertexFormatPtr vf_ptr = MNEW(VertexFormat(mRCPtr.ptr())); CHECK(vf_ptr);
				vf_ptr->load(&vertex_formats[0]);
				batch.primitive->setVertexFormat(vf_ptr.ptr());

				// locate the primitive
				batch.primitive->locate(0,vertex_count,0);

				// get the constant of the bone
				batch.bone = (Constant*)batch.primitive->getConstant("gBone");

				// add the new batch to the table
				mBatches.push_back(batch);
			}
			else
			{
				MAssert( VA("The bone`s keyword(%s) is unknown!", mesh_child_config_ptr->getValue()) );
			}
		}
	}

	/*
	====================
	draw
	====================
	*/
	INLINE VOID Mesh::draw( const View* view )
	{
		CHECK(mRCPtr!=NULL);

		// compute the bones
		std::map< Str, Bone >::iterator it = mBones.begin(), it_end = mBones.end();
		while(it != it_end)
		{
			it->second.pose = it->second.base;
			if(it->second.source) it->second.pose.postMult((*it->second.source));
			it++;
		}

		// draw the batches
		U32 batch_size = mBatches.size();
		for(U32 i = 0; i < batch_size; i++)
		{
			Batch& batch = mBatches[i];

			// cull the bounding box
			
			// update the constants of the bone
			if( batch.bone )
			{
				std::vector<Matrix>bones;
				U32 bone_size = batch.bones.size();
				for(U32 j = 0; j < bone_size; j++) bones.push_back(*batch.bones[j]);
				batch.bone->setMatrixArray(&bones[0],bones.size());
			}

			mRCPtr->exec( batch.primitive.ptr() );
		}
	}

//////////////////////////////////////////////////////////////////////////

NAMESPACE_END

//////////////////////////////////////////////////////////////////////////

